﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Security.Cryptography;
using System.Net.Mail;
using System.IO;

namespace JobSeek.Classes
{
    public class MyMembershipProvider
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="username">Can be username of Advertiser OR email or Applicant.</param>
        /// <param name="oldPassword"></param>
        /// <param name="newPassword"></param>
        /// <param name="role"></param>
        /// <returns></returns>
        public bool ChangePassword(string username, string oldPassword, string newPassword, string role)
        {
            bool success = false;
            string email = "";
            string salt = CreateSalt(new Random().Next(1, 100));
            switch (role)
            {
                case "Advertiser":
                    using (job_seekerEntities ctx = new job_seekerEntities())
                    {
                       
                        Advertiser advertiser = (from emp in ctx.Advertisers
                                         where emp.username == username
                                         where emp.password == oldPassword
                                         select emp).SingleOrDefault<Advertiser>();
                        if (advertiser != null)
                        {

                            string hashPwdAndSalt = CreateHashPassword(newPassword, salt);

                            advertiser.password = hashPwdAndSalt;

                            try
                            {
                                ctx.SaveChanges();
                                email = advertiser.email;
                                success = true;
                            }
                            catch (Exception ex)
                            {
                                //log error here;
                                success = false;
                            }
                        }
                    }
                    break;

                case "Applicant":
                    using (job_seekerEntities ctx = new job_seekerEntities())
                    {

                        Applicant applicant = (from emp in ctx.Applicants
                                               where emp.email == username
                                               where emp.password == oldPassword
                                               select emp).SingleOrDefault<Applicant>();

                        string hashPwdAndSalt = CreateHashPassword(newPassword, salt);

                        if (applicant != null)
                        {
                            applicant.password = hashPwdAndSalt;

                            try
                            {
                                ctx.SaveChanges();
                                email = applicant.email;
                                success = true;
                            }
                            catch (Exception ex)
                            {
                                //Log error here!
                                success = false;
                            }
                        }
                    }

                    break;
                case "Administrator":
                    break;
                default:
                    success = false;
                    break;
            }

            if (success)
            {
                string body = "Your password has been change.";
                string subject = "Password changed";

                SendEmail(body, subject, email);
            }
            return success;
        }

        private static string CreateSalt(int size)
        {
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            byte[] bytes = new byte[size];

            rng.GetBytes(bytes);

            return Convert.ToBase64String(bytes);
        }
        /// <summary>
        /// Generate a hash password for the new password. Password will be hashed with a auto generate salt.
        /// </summary>
        /// <param name="password">new password</param>
        /// <param name="User"></param>
        /// <param name="ctx"></param>
        /// <returns>return a hashed password</returns>
        private static string CreateHashPassword(string password, string salt)
        {

            string saltAndPwd = salt + password;

            string hashPwdAndSalt = FormsAuthentication.HashPasswordForStoringInConfigFile(saltAndPwd, "SHA1");

            return hashPwdAndSalt;


        }

        public Applicant CreateApplicant(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
        {
            throw new NotImplementedException();
        }

        
        public static bool CreateAdvertiser(Advertiser advertiser)
        {
            string salt = CreateSalt(new Random().Next(1,100));
            string hashedPwd=CreateHashPassword(advertiser.password, salt);
            advertiser.password = hashedPwd;
            advertiser.salt = salt;

            advertiser.pwd_attempts = 0;
            advertiser.account_disabled = false;
            
            using (job_seekerEntities ctx = new job_seekerEntities())
            {
                ctx.AddToAdvertisers(advertiser);
                try
                {                     
                    ctx.SaveChanges();

                    return true;
                }
                catch (Exception ex)
                {
                    return false;
                }
            }

        }

        public bool SetAdvertiserInactive(string username)
        {
            //if account expire, set to inactive
            try
            {
                using (job_seekerEntities ctx = new job_seekerEntities())
                {
                    Advertiser advertiser = (from emp in ctx.Advertisers
                                     where emp.username == username
                                             select emp).SingleOrDefault<Advertiser>();
                    if (advertiser != null)
                    {
                        advertiser.account_disabled = true;

                        ctx.SaveChanges();
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            catch (Exception ex)
            {
                //Log error here!
                return false;
            }
        }

        /// <summary>
        /// Reset password for Advertiser
        /// </summary>
        /// <param name="email">email when they registered</param>
        /// <returns>true if the password is successfully reset</returns>
        public bool ResetPasswordForAdvertiser(string email)
        {
            bool success = false;
            //send pwd to user.
            //generate a alphanumeric pwd to user and ask them to change on login
            string randPassword = GenerateRandPassword();
            string salt = CreateSalt(new Random().Next(1, 100));
            using (job_seekerEntities ctx = new job_seekerEntities())
            {

                Advertiser advertiser = (from emp in ctx.Advertisers
                                 where emp.email == email
                                 select emp).SingleOrDefault<Advertiser>();

                if (advertiser != null)
                {
                    string hashPwd = CreateHashPassword(randPassword, salt);

                    advertiser.password = hashPwd;

                    try
                    {
                        ctx.SaveChanges();
                        success = true;
                    }
                    catch (Exception ex)
                    {
                        success = false;
                    }
                    //send email to them telling them that thier password has change.
                }

                if (success)
                {
                    string body = new StreamReader(HttpContext.Current.Server.MapPath("~/Email_Template/reset_pwd.htm")).ReadToEnd().Replace("%Message%", randPassword);
                    //string body = "Your password has been reset. Please login with this new password and change it immediately after login.";
                    string subject = "Password Resetted";

                    //might give error if smtp server has error. Catch the exception in SendEmail()
                    SendEmail(body, subject, advertiser.email);
                }
            }
            return success;

        }
        /// <summary>
        /// Reset password if applicant forgets
        /// </summary>
        /// <param name="email">email when they register</param>
        /// <returns>true if the password is successfully reset</returns>
        public bool ResetPasswordForApplicant(string email)
        {
            //send pwd to user.
            //generate a alphanumeric pwd to user and ask them to change on login
            bool success = false;
            string randPassword = GenerateRandPassword();
            string salt = CreateSalt(new Random().Next(1, 100));

            using (job_seekerEntities ctx = new job_seekerEntities())
            {

                Applicant applicant = (from app in ctx.Applicants
                                       where app.email == email
                                       select app).SingleOrDefault<Applicant>();

                if (applicant != null)
                {
                    string hashPwd = CreateHashPassword(randPassword, salt);

                    applicant.password = hashPwd;
                    try
                    {
                        ctx.SaveChanges();
                        success = true;
                    }
                    catch (Exception ex)
                    {
                        //Log Error
                        success = false;
                    }
                    //send email to them telling them that thier password has changed
                }
                if (success)
                {

                    string body1 = new StreamReader(HttpContext.Current.Server.MapPath("~/Email_Template/reset_pwd.htm")).ReadToEnd();
                    string body = "Your password has been reset. Please login with this new password and change it immediately after login.";
                    string subject = "Password Resetted";
                    //might give error if smtp server has error. Catch the exception in SendEmail()
                    SendEmail(body, subject, applicant.email);
                }

            }
            return success;


        }
        /// <summary>
        /// Generate a random password
        /// </summary>
        /// <returns>returns random password</returns>
        public string GenerateRandPassword()
        {
            var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

            Random rand = new Random();

            string randPassword = new string(Enumerable.Repeat(chars, 8).Select(s => s[rand.Next(s.Length)]).ToArray());

            return randPassword;
        }

        /// <summary>
        /// Validate applicant upon logging in. 
        /// </summary>
        /// <param name="username">email address of the applicant.</param>
        /// <param name="password">password of the applicant</param>
        /// <returns>true if user is found.</returns>
        public bool ValidateApplicant(string username, string password)
        {
            bool success = false;
            //get salt from the database
            using (job_seekerEntities ctx = new job_seekerEntities())
            {
                Applicant applicant = (from app in ctx.Applicants
                                       where app.email == username
                                       select app).SingleOrDefault<Applicant>();

                if (applicant != null)
                {
                    string salt = applicant.salt;

                    string hashSaltAndPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(salt + password, "SHA1");

                    if (applicant.password == hashSaltAndPassword)
                    {
                        success = true;
                    }
                    else
                    {
                        //lock account if pwd attempt exceeds
                        applicant.pwd_attempts += 1;
                        if (applicant.pwd_attempts == 5)
                        {
                            applicant.account_disabled = 1;
                        }
                        success = false;
                    }

                }
            }

            return success;

        }
        /// <summary>
        /// Validate Advertiser upon logging in
        /// </summary>
        /// <param name="username">username of employers when they register</param>
        /// <param name="password">password</param>
        /// <returns>true if Advertiser is found</returns>
        public bool ValidateAdvertiser(string username, string password)
        {
            bool success = false;
            //get salt from the database
            using (job_seekerEntities ctx = new job_seekerEntities())
            {
                Advertiser Advertiser = (from emp in ctx.Advertisers
                                 where emp.username == username
                                 select emp).SingleOrDefault<Advertiser>();

                if (Advertiser != null)
                {
                    string salt = Advertiser.salt;

                    string hashSaltAndPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(salt + password, "SHA1");

                    if (Advertiser.password == hashSaltAndPassword)
                    {
                        success = true;
                    }
                    else
                    {
                        //incrememt pwd attempt by 1. If pwd attempt reaches 5, user account will be lock until futher notice.
                        Advertiser.pwd_attempts += 1;

                        if (Advertiser.pwd_attempts == 5)
                        {
                            //will be unable to login
                            Advertiser.account_disabled = true;
                        }
                        success = false;
                    }

                }
            }

            return success;

        }


        public bool SendEmail(string body, string subject, string email)
        {

            MailMessage mailMessage = new MailMessage();
            mailMessage.To.Add(email);
            mailMessage.From = new MailAddress("");
            mailMessage.Body = body;
            mailMessage.IsBodyHtml = true;
            mailMessage.Subject = subject;

            SmtpClient smtp = new SmtpClient("smtp.gmail.com");
            smtp.UseDefaultCredentials = false;
            smtp.EnableSsl = true;
            smtp.Credentials = new System.Net.NetworkCredential("", "");
            smtp.Port = 587;
            try
            {
                smtp.Send(mailMessage);
                return true;
            }
            catch (Exception ex)
            {
                //Log error here!
                return false;
            }
        }

        public static bool CheckAdvertiser(string username)
        {
            using (job_seekerEntities ctx = new job_seekerEntities())
            {
                var result = ctx.Advertisers.SingleOrDefault(ad => ad.username == username);

                if (result != null)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
        }
        /// <summary>
        /// Log errors in a file
        /// </summary>
        /// <param name="message">Message of the error. Inner exception or stacktrace</param>
        /// <param name="dateTime">time and date of the incident</param>
        public void LogCat(string message, DateTime dateTime)
        {

        }
    }
}